package com.gitlab.techschool.pcbook.service;

import com.gitlab.techschool.pcbook.pb.*;
import com.gitlab.techschool.pcbook.sample.Generator;
import com.google.protobuf.ByteString;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 测试createLaptop方法
 */
public class LaptopClient {

    private static final Logger LOGGER = Logger.getLogger(LaptopClient.class.getName());

    private final ManagedChannel Channel;
    private final LaptopServiceGrpc.LaptopServiceBlockingStub blockingStub;
    private final LaptopServiceGrpc.LaptopServiceStub asyncStub;

    public LaptopClient(String host, int port) {
        this.Channel = ManagedChannelBuilder.forAddress(host, port)
                .usePlaintext()
                .build();
        blockingStub = LaptopServiceGrpc.newBlockingStub(Channel)
                // 请求超时时间
                .withDeadlineAfter(5, TimeUnit.SECONDS);
        asyncStub = LaptopServiceGrpc.newStub(Channel);
    }

    public void shutdown() throws InterruptedException {
        Channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }

    /**
     * 创建笔记本(unary)
     */
    public void createLaptop(Laptop laptop) {
        CreateLaptopRequest request = CreateLaptopRequest.newBuilder().setLaptop(laptop).build();
        CreateLaptopResponse response = CreateLaptopResponse.getDefaultInstance();
        try {
            response = blockingStub.createLaptop(request);
        } catch (StatusRuntimeException e) {
            if (Status.Code.ALREADY_EXISTS.equals(e.getStatus().getCode())) {
                LOGGER.info("laptop ID already exists: " + laptop.getId());
                return;
            }
            LOGGER.log(Level.SEVERE, "request failed: " + e.getMessage());
            return;
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "request failed: " + e.getMessage());
            return;
        }

        LOGGER.info("request created with ID: " + response.getId());
    }

    /**
     * 按条件搜索笔记本(server stream)
     */
    public void searchLaptop(Filter filter) {
        SearchLaptopRequest request = SearchLaptopRequest.newBuilder()
                .setFilter(filter)
                .build();
        Iterator<SearchLaptopResponse> iterator = blockingStub.searchLaptop(request);
        while (iterator.hasNext()) {
            SearchLaptopResponse response = iterator.next();
            Laptop laptop = response.getLaptop();
            LOGGER.info("- found: " + laptop.getId());
        }
    }

    /**
     * 上传图片(client stream)
     */
    public void uploadImage(String laptopId, String imagePath) throws InterruptedException {
        // 因为响应是异步的，所以用CountDownLatch计数
        CountDownLatch finishLatch = new CountDownLatch(1);
        StreamObserver<UploadImageRequest> requestObserver = asyncStub.withDeadlineAfter(5, TimeUnit.SECONDS)
                .uploadImage(new StreamObserver<UploadImageResponse>() {
                    @Override
                    public void onNext(UploadImageResponse value) {
                        LOGGER.info("receive response:\n" + value);
                    }

                    @Override
                    public void onError(Throwable t) {
                        LOGGER.log(Level.SEVERE, "upload failed: " + t.getMessage());
                        finishLatch.countDown();
                    }

                    @Override
                    public void onCompleted() {
                        LOGGER.info("image uploaded");
                        finishLatch.countDown();
                    }
                });

        // 读取图片
        FileInputStream fileInputStream;
        try {
            fileInputStream = new FileInputStream(imagePath);
        } catch (FileNotFoundException e) {
            LOGGER.log(Level.SEVERE, "cannot read image file: " + e.getMessage());
            return;
        }

        // 先发送ImageInfo
        String imageType = imagePath.substring(imagePath.lastIndexOf(".") + 1);
        ImageInfo info = ImageInfo.newBuilder()
                .setLaptopId(laptopId)
                .setImageType(imageType)
                .build();
        UploadImageRequest request = UploadImageRequest.newBuilder()
                .setInfo(info)
                .build();

        try {
            requestObserver.onNext(request);
            LOGGER.info("send image info:\n" + info);

            // 再发送chunk_data
            byte[] buffer = new byte[1024];

            while (true) {
                int n = fileInputStream.read(buffer);
                if (n == -1) {
                    break;
                }

                if (finishLatch.getCount() == 0) {
                    return;
                }
                request = UploadImageRequest.newBuilder()
                        .setChunkData(ByteString.copyFrom(buffer, 0, n))
                        .build();
                requestObserver.onNext(request);
                LOGGER.info("send image chunk with size: " + n);
            }

        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "unexpect error: " + e.getMessage());
            requestObserver.onError(e);
            return;
        }

        requestObserver.onCompleted();

        if (!finishLatch.await(1, TimeUnit.MINUTES)) {
            LOGGER.warning("request cannot finish within 1 minute");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        LaptopClient client = new LaptopClient("localhost", 8080);

        Generator generator = new Generator();

        try {
//            测试create和search
//            // 创建10个laptop
//            for (int i = 0; i < 10; i++) {
//                Laptop laptop = generator.newLaptop();
//                client.createLaptop(laptop);
//            }
//
//            Memory minRam = Memory.newBuilder()
//                    .setValue(8)
//                    .setUnit(Memory.Unit.GIGABYTE)
//                    .build();
//            Filter filter = Filter.newBuilder()
//                    .setMaxPriceUsd(3000)
//                    .setMinCpuCores(4)
//                    .setMinCpuGhz(2.5)
//                    .setMinRam(minRam)
//                    .build();
//
//            // 搜索laptop
//            client.searchLaptop(filter);

//            测试uploadImage
            Laptop laptop = generator.newLaptop();
            client.createLaptop(laptop);
            client.uploadImage(laptop.getId(), "tmp/laptop.jpg");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            client.shutdown();
        }
    }

}
